package com.dhl.fin.api.service;

import cn.hutool.core.util.IdUtil;
import com.dhl.fin.api.common.util.JsonUtil;
import com.dhl.fin.api.common.util.SpringContextUtil;
import com.dhl.fin.api.common.util.WebUtil;
import com.dhl.fin.api.config.WeiXinPayConfig;
import com.dhl.fin.api.dto.QueryPayOrderResponseDTO;
import com.dhl.fin.api.dto.RisePayOrderResponseDTO;
import com.dhl.fin.api.dto.WeiXinRefundDTO;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import java.util.Map;

/**
 * @author CuiJianbo
 * @since 2025/3/27
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class WeixinPayService {

    private static final String MONEY_UNIT = "CNY";

    private WeiXinPayConfig weiXinPayConfig;

    private JsapiService jsapiService;

    private RefundService refundService;

    @Autowired
    private PaymentRecordService paymentRecordService;

    {
        weiXinPayConfig = SpringContextUtil.getBean(WeiXinPayConfig.class);

        Config config =
                new RSAPublicKeyConfig.Builder()
                        .publicKeyId(weiXinPayConfig.getPublicKeyId())
                        .publicKey(weiXinPayConfig.getPublicKey())
                        .merchantId(weiXinPayConfig.getMerchantId())
                        .privateKeyFromPath(weiXinPayConfig.getPrivateKeyPath())
                        .merchantSerialNumber(weiXinPayConfig.getMerchantSerialNumber())
                        .apiV3Key(weiXinPayConfig.getApiV3key())
                        .build();


        jsapiService = new JsapiService.Builder().config(config).build();

        refundService = new RefundService.Builder().config(config).build();


    }


    public String risePayOrder(Double prePrice, String description, String outTradeNo) {
        PrepayRequest prepayRequest = new PrepayRequest();
        Integer mount = Integer.valueOf(String.format("%.0f", prePrice * 100));
        Payer payer = new Payer();
        Amount amount = new Amount();
        amount.setTotal(mount);
        payer.setOpenid(WebUtil.getLoginUser().getUuid());
        prepayRequest.setAmount(amount);
        prepayRequest.setPayer(payer);
        prepayRequest.setAppid(weiXinPayConfig.getAppId());
        prepayRequest.setMchid(weiXinPayConfig.getMerchantId());
        prepayRequest.setDescription(description);
        prepayRequest.setNotifyUrl(weiXinPayConfig.getPayCallBackUrl());
        prepayRequest.setOutTradeNo(outTradeNo);
        PrepayResponse response = jsapiService.prepay(prepayRequest);
        return response.getPrepayId();
    }

    /**
     * 退款明细
     */
    public void refundOrder(WeiXinRefundDTO refundDTO) throws Exception {

        String refundOrderNumber = IdUtil.simpleUUID();
        CreateRequest request = new CreateRequest();
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(refundDTO.getAmount());
        amountReq.setCurrency(MONEY_UNIT);
        amountReq.setTotal(refundDTO.getAmount());

        request.setAmount(amountReq);
        request.setReason(refundDTO.getReason());
        request.setTransactionId(refundDTO.getTransactionNo());
        request.setNotifyUrl(weiXinPayConfig.getPayCallBackUrl());
        request.setOutRefundNo(refundOrderNumber);
        request.setOutTradeNo(refundDTO.getOutTradeNo());

        //发起退款
        refundService.create(request);

        //更新支付订单状态为已退款
        paymentRecordService.changePayOrderStatusToRefund(refundDTO.getOutTradeNo(),refundDTO.getTransactionNo());

    }



    /**
     * 按照订单号查询订单状态
     */
    public QueryPayOrderResponseDTO queryStatusByOrderNumber(String outTradeNo) {
        QueryOrderByOutTradeNoRequest byOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
        byOutTradeNoRequest.setMchid(weiXinPayConfig.getMerchantId());
        byOutTradeNoRequest.setOutTradeNo(outTradeNo);
        Transaction transaction = jsapiService.queryOrderByOutTradeNo(byOutTradeNoRequest);
        return QueryPayOrderResponseDTO.builder()
                .transactionId(transaction.getTransactionId())
                .status(transaction.getTradeState().name())
                .build();
    }

    /**
     * 解密微信回调信息
     */
    public Map decryptCiphertext(String ciphertext, String nonce, String associatedData) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException, InvalidKeyException {
        // 初始化Cipher对象，使用AES/GCM/NoPadding模式
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        // 将APIv3密钥转换成密钥对象
        SecretKeySpec keySpec = new SecretKeySpec(weiXinPayConfig.getApiV3key().getBytes("UTF-8"), "AES");
        // 创建GCM参数对象
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, nonce.getBytes("UTF-8"));
        // 初始化Cipher为解密模式
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
        // 设置附加认证数据
        cipher.updateAAD(associatedData.getBytes("UTF-8"));
        // Base64解码加密数据
        byte[] encryptedDataBytes = Base64.getDecoder().decode(ciphertext);
        // 执行解密操作
        byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
        // 将解密后的字节数组转换成字符串
        return JsonUtil.parseToMap(new String(decryptedBytes, "UTF-8"));
    }

    /**
     * 手动生成支付签名
     *
     * @param prepayId
     * @return
     * @throws Exception
     */
    public RisePayOrderResponseDTO createJsapiSignature(String prepayId, String outTradeNo) throws Exception {
        String appId = weiXinPayConfig.getAppId();
        PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(weiXinPayConfig.getPrivateKeyPath());
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = RandomStringUtils.randomAlphanumeric(32);
        String packageStr = "prepay_id=" + prepayId;

        // 构造签名串
        String message = appId + "\n" +
                timeStamp + "\n" +
                nonceStr + "\n" +
                packageStr + "\n";

        // 使用商户私钥签名
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initSign(privateKey);
        signer.update(message.getBytes(StandardCharsets.UTF_8));
        byte[] signature = signer.sign();

        // Base64编码
        String sign = Base64.getEncoder().encodeToString(signature);

        return RisePayOrderResponseDTO.builder()
                .appId(appId)
                .timeStamp(timeStamp)
                .nonceStr(nonceStr)
                .packageStr(packageStr)
                .signType("RSA")
                .paySign(sign)
                .outTradeNo(outTradeNo)
                .prepayId(prepayId)
                .build();

    }

}
